package drds.common.db.xml_mapping.parsing;

import lombok.Getter;
import lombok.Setter;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;


public class XmlNode {
  @Setter
  @Getter
  private final XmlNodeParser xmlNodeParser;
  @Setter
  @Getter
  private final org.w3c.dom.Node node;
  @Setter
  @Getter
  private final String name;
  @Setter
  @Getter
  private final Properties attributes;
  @Setter
  @Getter
  private final String body;
  //
  @Setter
  @Getter
  private final Properties variables;

  public XmlNode(XmlNodeParser xmlNodeParser, org.w3c.dom.Node node, Properties variables) {
    this.xmlNodeParser = xmlNodeParser;
    this.node = node;
    this.name = node.getNodeName();
    this.attributes = parseAttributes(node);
    this.body = parseBody(node);
    //
    this.variables = variables;

  }


  //
  public XmlNode getParentXmlNode() {
    org.w3c.dom.Node parentNode = node.getParentNode();
    if (!(parentNode instanceof Element)) {
      return null;
    } else {
      return new XmlNode(xmlNodeParser, parentNode, variables);
    }
  }

  public XmlNode newXmlNode(org.w3c.dom.Node node) {
    return new XmlNode(xmlNodeParser, node, variables);
  }

  //
  public String getValueBasedIdentifier() {
    StringBuilder sb = new StringBuilder();
    XmlNode current = this;
    while (current != null) {
      if (current != this) {
        sb.insert(0, "_");
      }
      String value = current.getStringAttribute("id",
        current.getStringAttribute("value",
          current.getStringAttribute("property", null)));
      if (value != null) {
        value = value.replace('.', '_');
        sb.insert(0, "]");
        sb.insert(0,
          value);
        sb.insert(0, "[");
      }
      sb.insert(0, current.getName());
      current = current.getParentXmlNode();
    }
    return sb.toString();
  }

  //
  public List<XmlNode> evaluateXmlNodeList(String expression) {
    return xmlNodeParser.evaluateXmlNodeList(node, expression);
  }

  public XmlNode evaluateNode(String expression) {
    return xmlNodeParser.evaluateNode(node, expression);
  }

  //
  public String evaluateString(String expression) {

    return xmlNodeParser.evaluateString(node, expression);
  }

  public Boolean evaluateBoolean(String expression) {
    return xmlNodeParser.evaluateBoolean(node, expression);
  }

  public Float evaluateFloat(String expression) {
    return xmlNodeParser.evaluateFloat(node, expression);
  }

  public Double evaluateDouble(String expression) {
    return xmlNodeParser.evaluateDouble(node, expression);
  }

  //
  public String getStringBody() {
    return getStringBody(null);
  }

  public String getStringBody(String defaultValue) {
    if (body == null) {
      return defaultValue;
    } else {
      return body;
    }
  }

  public Boolean getBooleanBody() {
    return getBooleanBody(null);
  }

  public Boolean getBooleanBody(Boolean defaultValue) {
    if (body == null) {
      return defaultValue;
    } else {
      return Boolean.valueOf(body);
    }
  }

  public Integer getIntBody() {
    return getIntBody(null);
  }

  public Integer getIntBody(Integer defaultValue) {
    if (body == null) {
      return defaultValue;
    } else {
      return Integer.parseInt(body);
    }
  }

  public Long getLongBody() {
    return getLongBody(null);
  }

  public Long getLongBody(Long defaultValue) {
    if (body == null) {
      return defaultValue;
    } else {
      return Long.parseLong(body);
    }
  }

  //
  private Properties parseAttributes(org.w3c.dom.Node node) {
    Properties attributes = new Properties();
    NamedNodeMap attributeNodes = node.getAttributes();
    if (attributeNodes != null) {
      for (int i = 0; i < attributeNodes.getLength(); i++) {
        org.w3c.dom.Node attribute = attributeNodes.item(i);
        String value = PropertyParser.parse(attribute.getNodeValue(), variables);
        attributes.put(attribute.getNodeName(), value);
      }
    }
    return attributes;
  }

  public String getStringAttribute(String name) {
    return getStringAttribute(name, null);
  }

  public String getStringAttribute(String name, String defaultValue) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return defaultValue;
    } else {
      return value;
    }
  }

  public Boolean getBooleanAttribute(String name) {
    return getBooleanAttribute(name, null);
  }

  public Boolean getBooleanAttribute(String name, Boolean defaultValue) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return defaultValue;
    } else {
      return Boolean.valueOf(value);
    }
  }

  public Integer getIntAttribute(String name) {
    return getIntAttribute(name, null);
  }

  public Integer getIntAttribute(String name, Integer defaultValue) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return defaultValue;
    } else {
      return Integer.parseInt(value);
    }
  }

  public Long getLongAttribute(String name) {
    return getLongAttribute(name, null);
  }

  public Long getLongAttribute(String name, Long defaultValue) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return defaultValue;
    } else {
      return Long.parseLong(value);
    }
  }

  public Float getFloatAttribute(String name) {
    return getFloatAttribute(name, null);
  }

  public Float getFloatAttribute(String name, Float defaultValue) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return defaultValue;
    } else {
      return Float.parseFloat(value);
    }
  }

  public Double getDoubleAttribute(String name) {
    return getDoubleAttribute(name, null);
  }

  public Double getDoubleAttribute(String name, Double defaultValue) {
    String value = attributes.getProperty(name);
    if (value == null) {
      return defaultValue;
    } else {
      return Double.parseDouble(value);
    }
  }

  //
  public List<XmlNode> getChildrenXmlNodeList() {
    List<XmlNode> xmlNodeList = new ArrayList<>();
    NodeList nodeList = node.getChildNodes();
    if (nodeList != null) {
      for (int i = 0, index = nodeList.getLength(); i < index; i++) {
        org.w3c.dom.Node node = nodeList.item(i);
        if (node.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
          xmlNodeList.add(new XmlNode(xmlNodeParser, node, variables));
        }
      }
    }
    return xmlNodeList;
  }

  public Properties getChildrenXmlNodeListAsProperties() {
    Properties properties = new Properties();
    for (XmlNode xmlNode : getChildrenXmlNodeList()) {
      String name = xmlNode.getStringAttribute("name");
      String value = xmlNode.getStringAttribute("value");
      if (name != null && value != null) {
        properties.setProperty(name, value);
      }
    }
    return properties;
  }
  //


  private String parseBody(org.w3c.dom.Node node) {
    String data = getBodyData(node);
    if (data == null) {
      NodeList nodeList = node.getChildNodes();
      for (int index = 0; index < nodeList.getLength(); index++) {
        org.w3c.dom.Node childNode = nodeList.item(index);
        data = getBodyData(childNode);
        if (data != null) {
          break;
        }
      }
    }
    return data;
  }

  private String getBodyData(org.w3c.dom.Node node) {
    if (node.getNodeType() == org.w3c.dom.Node.CDATA_SECTION_NODE//
      ||//
      node.getNodeType() == org.w3c.dom.Node.TEXT_NODE) {//
      String data = ((CharacterData) node).getData();
      data = PropertyParser.parse(data, variables);
      return data;
    }
    return null;
  }

}
